home *** CD-ROM | disk | FTP | other *** search
/ Windows 95 Super Collection / Windows 95 Super Collection.iso / win95 / bench / thread / thrdomtr.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-08-13  |  34.9 KB  |  1,325 lines

  1. //==============================================================================
  2. // THRDOMTR.CPP : Main source for THRDOMTR.EXE 'Thread-o-meter' multi-threading 
  3. //                      utility for use with Windows 95 and Windows NT.
  4. //                    
  5. //                        (Shows off Win32 multi-threading including some synchronization API.)
  6. //
  7. //                                            v. 1.00
  8. //
  9. //                         'What's the Code?', September 1995, Computer Shopper.
  10. //                                                                                              
  11. //                Compiler: Microsoft Visual C++ 2,x  -- (Should compile
  12. //                          with any compiler licensing MFC 3.0 or greater)
  13. //
  14. //
  15. //                        by Richard Dragan (CIS) 74743,1510 
  16. //                                             (WWW) 74743.1510@compuserve.com                        
  17. //                      
  18. //==============================================================================
  19. #include <process.h>
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22.  
  23. #include "stdafx.h"
  24. #include "resource.h"
  25.  
  26. #include "thrdtsts.h"
  27. #include "fpcalc.h"
  28.  
  29. #include "thrdoptn.h"
  30. #include "fileopts.h"
  31. #include "thrdomtr.h"
  32.  
  33. // Used for data to write for file I/O demo.
  34. char achGlobalBuff[BUFF_SIZE];
  35.  
  36. char szGlobalMutexName[] = "ThreadCollectionMutex";
  37.  
  38. // Accessed by competing threads.
  39. CRITICAL_SECTION CriticalSectionObj;
  40.  
  41. BOOL bKillingAllThreads = FALSE;
  42.  
  43. // These two globals are accessed by all threads in the thread demo.
  44. // Coordinated access (mutual exclusion) is provided by the mutex object -OR-
  45. // critical sections, depending on which settings you
  46. //------------------------------------------------------------------------------
  47. DWORD dwGlobalThreadLastTimeStamp = 0L;  // Contains timestamp (in system 
  48.                                                            // ticks) of last thread to finish.
  49. int nGlobalThreadFinishedCount = 0L;         // A count of finished threads.
  50. //------------------------------------------------------------------------------
  51.  
  52. HINSTANCE hInst = NULL;
  53.  
  54.  
  55. // Dialog box procedure used by thread-in-a-window tests.
  56. BOOL CALLBACK ThreadDlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  57. {
  58.     int nId = LOWORD(wParam);
  59.     char szCaption[40];
  60.  
  61.     switch(uMsg)
  62.     {
  63.         case PM_START_THREAD_PROCESSING:  // Signal we should start processing.
  64.             wsprintf(szCaption, "Thread #%d", (int)LOWORD(lParam));
  65.             SetWindowText(hdlg, szCaption);
  66.  
  67.             if(wParam == 1)
  68.             {    
  69.                 SetDlgItemText(hdlg, IDC_THRD_WIN_PROMPT, "Calculating 1000 Primes...");    
  70.                 UpdateWindow(GetDlgItem(hdlg, IDC_THRD_WIN_PROMPT));
  71.             }
  72.             else if(wParam == 2)
  73.             {    
  74.                 SetDlgItemText(hdlg, IDC_THRD_WIN_PROMPT, "Floating point calculations...");    
  75.                 UpdateWindow(GetDlgItem(hdlg, IDC_THRD_WIN_PROMPT));
  76.             }
  77.             else if(wParam == 3)
  78.             {    
  79.                 // Timed graphics.
  80.                 // Hide our static prompt.
  81.                 // Re-init. random generator (otherwise, windows show the same stuff.)
  82.                 srand((UINT)GetTickCount());
  83.                 ShowWindow(GetDlgItem(hdlg, IDC_THRD_WIN_PROMPT), SW_HIDE);
  84.             }
  85.            else // Untimed graphics.
  86.             {    
  87.                 // Hide our static prompt.
  88.                 srand((UINT)GetTickCount());
  89.                 ShowWindow(GetDlgItem(hdlg, IDC_THRD_WIN_PROMPT), SW_HIDE);
  90.             }
  91.             return TRUE;
  92.  
  93.         case WM_COMMAND:
  94.             switch(nId)
  95.             {
  96.                 case IDOK:
  97.                 case IDCANCEL:
  98.                     DestroyWindow(hdlg);        
  99.                 default: return FALSE;
  100.             }
  101.  
  102.         case WM_CLOSE:
  103.             DestroyWindow(hdlg);
  104.             return TRUE;
  105.  
  106.     }    
  107.     return FALSE;
  108. }
  109.  
  110. void FileWriteThreadProc(void *pParams)
  111. {
  112.     FILETHREADPARAMS *pThrdParams = (FILETHREADPARAMS *)pParams;
  113.  
  114.     ASSERT (pParams);
  115.  
  116.     // Make copy of our params. so we are re-entrant.
  117.     FILETHREADPARAMS MyParams;
  118.  
  119.     memcpy(&MyParams, pThrdParams, sizeof(FILETHREADPARAMS));
  120.         
  121.     const int UPDATE_PROGRESS = 6;  // Every 6 x 4KB = 24KB, update status bar.
  122.     int nProgressCount = 0;
  123.  
  124.     char szMsg[256];
  125.  
  126.     float fPercentDone;
  127.  
  128.     // Write specified # of bytes to a temporary file.
  129.     DWORD dwTotalBytesDone = 0L;    
  130.     DWORD dwBytesToGo = 0L;    
  131.  
  132.     WORD uBytesToWrite = 0L;
  133.  
  134.     DWORD dwBytesThisPass = 0L;
  135.  
  136.     HFILE hFileOut = _lcreat(MyParams.szFileName, 0);
  137.  
  138.     if(hFileOut == HFILE_ERROR)
  139.     {
  140.         wsprintf(szMsg, "Can't open file '%s' for writing.", MyParams.szFileName); 
  141.         MessageBox(NULL, szMsg, "Can't Create File", MB_ICONSTOP);
  142.         if(MyParams.bIsAsync)
  143.         {
  144.             ::SendMessage(MyParams.hwndParent, PM_FILE_THREAD_ENDED, 0, FALSE);
  145.             _endthread();
  146.         }
  147.         return;
  148.     }
  149.  
  150.     // To here we have an open file.  Write required number of bytes.
  151.     // Update status bar in main window with percentage of save.
  152.     while(1)
  153.     {
  154.         dwBytesToGo = MyParams.dwTotalBytes - dwTotalBytesDone;
  155.  
  156.         // Write contents of entire buffer unless we're on last pass.        
  157.         if(dwBytesToGo < BUFF_SIZE)
  158.             uBytesToWrite = (UINT)dwBytesToGo;
  159.         else
  160.             uBytesToWrite = BUFF_SIZE;
  161.     
  162.         dwBytesThisPass = (DWORD)_lwrite(hFileOut, achGlobalBuff, uBytesToWrite);
  163.  
  164.         if(dwBytesThisPass != uBytesToWrite)
  165.         {
  166.             MessageBox(NULL, "The requested number of bytes could not be written to temp. file.  Ensure that you have enough free space on the C: drive for this demo. and try again.", "Can't Write To File", MB_ICONSTOP);
  167.             // Delete our file.
  168.             _lclose(hFileOut);
  169.             _unlink(MyParams.szFileName);
  170.             if(MyParams.bIsAsync)
  171.                 _endthread();
  172.             return;
  173.         }
  174.  
  175.         dwTotalBytesDone += dwBytesThisPass;
  176.  
  177.         if(dwTotalBytesDone >= MyParams.dwTotalBytes)
  178.             // We're finished.
  179.             break;     
  180.  
  181.         nProgressCount++;
  182.         if(nProgressCount >= UPDATE_PROGRESS);
  183.         {
  184.             // Then update our progress bar in parent window.
  185.             fPercentDone = (float)dwTotalBytesDone/(float)MyParams.dwTotalBytes * (float)100.0;
  186.             sprintf(szMsg, "Writing %u bytes (%3.1f%% done)...", MyParams.dwTotalBytes, fPercentDone);
  187.             
  188.             ::SendMessage(MyParams.hwndParent, PM_THREAD_STATUS_MSG, 0, (LPARAM)szMsg); 
  189.             nProgressCount = 0; // Reset counter.
  190.         }
  191.     }
  192.  
  193.     ::SendMessage(MyParams.hwndParent, PM_THREAD_STATUS_MSG, 0, (LPARAM)"(Data was written successfully.)"); 
  194.     
  195.     _lclose(hFileOut);
  196.     _unlink(MyParams.szFileName);
  197.     if(MyParams.bIsAsync)
  198.     {
  199.         ::SendMessage(MyParams.hwndParent, PM_FILE_THREAD_ENDED, 0, TRUE);
  200.         _endthread();   // Success
  201.     }
  202. }
  203.  
  204. void FileReadThreadProc(void *pParams)
  205. {
  206.     FILETHREADPARAMS *pThrdParams = (FILETHREADPARAMS *)pParams;
  207.  
  208.     ASSERT (pParams);
  209.  
  210.     char szMsg[256];
  211.  
  212.     // Make copy of our params. so we are re-entrant.
  213.     FILETHREADPARAMS MyParams;
  214.  
  215.     lstrcpy(MyParams.szFileName, pThrdParams->szFileName);
  216.         
  217.     MyParams.hwndParent = pThrdParams->hwndParent;
  218.     MyParams.bIsAsync = pThrdParams->bIsAsync;
  219.  
  220.     HFILE hFileIn = _lopen(MyParams.szFileName, OF_READ);
  221.  
  222.     if(hFileIn == HFILE_ERROR)
  223.     {
  224.         wsprintf(szMsg, "Can't open file '%s' for reading.  Make sure this path is correct and that is not in-use by another application and try again.", MyParams.szFileName); 
  225.         MessageBox(NULL, szMsg, "Can't Open File", MB_ICONSTOP);
  226.         if(MyParams.bIsAsync)
  227.         {
  228.             ::SendMessage(MyParams.hwndParent, PM_FILE_THREAD_ENDED, 0, FALSE);
  229.             _endthread();
  230.         }
  231.         return;
  232.     }
  233.  
  234.     // Otherwise, get size of this file.
  235.     MyParams.dwTotalBytes = (DWORD) _llseek(hFileIn, 0, FILE_END);    
  236.  
  237.     _llseek(hFileIn, 0, FILE_BEGIN);    
  238.  
  239.     const int UPDATE_PROGRESS = 4;  // Every 4 x 4KB = 16KB, update status bar.
  240.     int nProgressCount = 0;
  241.  
  242.     float fPercentDone;
  243.  
  244.     // Write specified # of bytes to a temporary file.
  245.     DWORD dwTotalBytesDone = 0L;    
  246.     DWORD dwBytesToGo = 0L;    
  247.  
  248.     WORD uBytesToRead = 0L;
  249.  
  250.     DWORD dwBytesThisPass = 0L;
  251.         
  252.     // To here we have an open file.  Read in all of this file.
  253.     // Update status bar in main window with percentage of read.
  254.     while(1)
  255.     {
  256.         dwBytesToGo = MyParams.dwTotalBytes - dwTotalBytesDone;
  257.  
  258.         // Read into entire buffer unless we're on last pass.        
  259.         if(dwBytesToGo < BUFF_SIZE)
  260.             uBytesToRead = (UINT)dwBytesToGo;
  261.         else
  262.             uBytesToRead = BUFF_SIZE;
  263.     
  264.         dwBytesThisPass = (DWORD)_lread(hFileIn, achGlobalBuff, uBytesToRead);
  265.  
  266.         if(dwBytesThisPass != uBytesToRead)
  267.         {
  268.             MessageBox(NULL, "The requested number of bytes could not be read.", "Can't Read From File", MB_ICONSTOP);
  269.             // Delete our file.
  270.             _lclose(hFileIn);
  271.             if(MyParams.bIsAsync)
  272.                 _endthread();
  273.             return;
  274.         }
  275.  
  276.         dwTotalBytesDone += dwBytesThisPass;
  277.  
  278.         if(dwTotalBytesDone >= MyParams.dwTotalBytes)
  279.             // We're finished.
  280.             break;     
  281.  
  282.         nProgressCount++;
  283.         if(nProgressCount >= UPDATE_PROGRESS);
  284.         {
  285.             // Then update our progress bar in parent window.
  286.             fPercentDone = (float)dwTotalBytesDone/(float)MyParams.dwTotalBytes * (float)100.0;
  287.             sprintf(szMsg, "Reading %u bytes (%3.1f%% done)...", MyParams.dwTotalBytes, fPercentDone);
  288.             
  289.             ::SendMessage(MyParams.hwndParent, PM_THREAD_STATUS_MSG, 0, (LPARAM)szMsg); 
  290.             nProgressCount = 0; // Reset counter.
  291.         }
  292.     }
  293.  
  294.     ::SendMessage(MyParams.hwndParent, PM_THREAD_STATUS_MSG, 0, (LPARAM)"(Data was read successfully.)"); 
  295.     
  296.     _lclose(hFileIn);
  297.     if(MyParams.bIsAsync)
  298.     {
  299.         ::SendMessage(MyParams.hwndParent, PM_FILE_THREAD_ENDED, 0, TRUE);
  300.         _endthread();   // Success
  301.     }
  302. }
  303.  
  304. void ThreadDemoProc(void *pParams)
  305. {
  306.     THREADPARAMS *pThrdParams = (THREADPARAMS *)pParams;
  307.  
  308.     HWND hwndMyThrd = NULL;
  309.         
  310.     THREADPARAMS MyParams;
  311.  
  312.     char szMsg[256];
  313.  
  314.     HANDLE hMutex = 0;
  315.  
  316.     // Make a copy of of params. so that we're guaranteed to be reentry.
  317.     memcpy(&MyParams, pThrdParams, sizeof(THREADPARAMS));
  318.  
  319.     // Free up this thread block. We've copied it locally.
  320.     HGLOBAL hGlobal = GlobalHandle(pParams);
  321.     GlobalUnlock(hGlobal);
  322.     GlobalFree(hGlobal);
  323.    
  324.     if(MyParams.bUseMutex)
  325.     {
  326.         hMutex = OpenMutex(SYNCHRONIZE, TRUE, szGlobalMutexName);
  327.         if(!hMutex)
  328.         {
  329.             wsprintf(szMsg, "Can't open mutex in Thread #%d.  'Critical sections' will be used instead", MyParams.nThreadNum);
  330.             MessageBox(NULL, szMsg, "Can't open mutex.", MB_ICONSTOP);
  331.             MyParams.bUseMutex = FALSE;
  332.         }    
  333.     }
  334.  
  335.     if(MyParams.bUseWindow)
  336.     {
  337.         hwndMyThrd = CreateDialog(hInst, 
  338.                                           MAKEINTRESOURCE(IDD_THREAD_WINDOW),
  339.                                           MyParams.hwndParent,
  340.                                           (DLGPROC)ThreadDlgProc);
  341.         if(!hwndMyThrd)
  342.         {
  343.            // Can't do this--we need a window to run graphics tests.
  344.             MessageBox(NULL, "Unable to create thread window.", "Can't Create Window", MB_ICONSTOP);
  345.             _endthread();
  346.             return;
  347.         }
  348.  
  349.         RECT rWin;
  350.         GetWindowRect(hwndMyThrd, &rWin);
  351.         MoveWindow(hwndMyThrd, 
  352.                         MyParams.ptWinStart.x,
  353.                         MyParams.ptWinStart.y,
  354.                         rWin.right - rWin.left,
  355.                         rWin.bottom - rWin.top,
  356.                         TRUE);
  357.  
  358.         ShowWindow(hwndMyThrd, SW_SHOWNORMAL);
  359.         UpdateWindow(hwndMyThrd);
  360.  
  361.         SendMessage(hwndMyThrd, 
  362.                         PM_START_THREAD_PROCESSING, 
  363.                         (WPARAM)MyParams.nDemoType,
  364.                         (LPARAM)MyParams.nThreadNum);
  365.     
  366.         UpdateWindow(hwndMyThrd);
  367.     }
  368.  
  369.     LONG *pPrimesArray = NULL;
  370.     UINT uPrimesTotal = 0;
  371.  
  372.     UINT uFloatingPtReps = 0;
  373.     UINT uGraphicsReps = 0;
  374.     
  375.     // Run requested type of test with or without windows.
  376.     if(MyParams.nDemoType == 1)
  377.     {
  378.         // 'Primes' requires a little additional initialization.
  379.         pPrimesArray = new long[1000];
  380.         
  381.         if(!pPrimesArray)
  382.         {
  383.             MessageBox(NULL, "Not enough memory for Primes test!.", "Not Enough Memory", MB_ICONSTOP);
  384.             if(hwndMyThrd)
  385.             {
  386.                 DestroyWindow(hwndMyThrd);
  387.                 UpdateWindow(hwndMyThrd);
  388.             }
  389.             _endthread();
  390.             return;
  391.         }        
  392.     }
  393.  
  394.     // Loop here.  (We break tests up into smaller pieces for better
  395.     // distribution of CPU timeslices.)    
  396.     
  397.     BOOL bContinue = TRUE;
  398.     
  399.     while(!bKillingAllThreads && bContinue)
  400.     {
  401.           MSG msg;
  402.          if(PeekMessage(&msg, 0, WM_KEYDOWN, WM_KEYDOWN, PM_NOREMOVE))
  403.         {
  404.             // 'ESC' to abort demo.
  405.             if(msg.wParam == VK_ESCAPE && !bKillingAllThreads)
  406.             {
  407.                 // If 'escape' is pressed, end this demo. from parent window.
  408.                 SendMessage(MyParams.hwndParent, WM_COMMAND, (WPARAM)ID_THREAD_DEMO, (LPARAM)0L);
  409.                 continue;
  410.             }
  411.         }    
  412.                 
  413.         switch(MyParams.nDemoType)
  414.         {
  415.             case 1: // Primes (1000 primes calculated per thread)
  416.                 uPrimesTotal = DoPrimesCalc(1000, 10, pPrimesArray, uPrimesTotal);
  417.                 if(uPrimesTotal >= 1000)
  418.                 {
  419.                      // Check that we did all our work.
  420.                     ASSERT (pPrimesArray[999] == 7919);
  421.                     
  422.                     bContinue = FALSE;
  423.                 }
  424.                 break;
  425.  
  426.             case 2: // Floating Point (20 Repetitions per thread)
  427.                 uFloatingPtReps = DoFloatingPointCalc(20, 2, uFloatingPtReps);
  428.                 if(uFloatingPtReps >= 20)
  429.                     bContinue = FALSE;
  430.                 break;
  431.                     
  432.             case 3: // Graphics (500 screen draws per thread)
  433.                 uGraphicsReps = DoGraphics(hwndMyThrd, 500, 8, uGraphicsReps);
  434.                 if(uGraphicsReps >= 500)
  435.                     bContinue = FALSE;
  436.                 break;
  437.  
  438.             case 4: // Graphics demo.  (Continue until we're stopped by user.)
  439.                 DoGraphics(hwndMyThrd, 0, 10, 0);
  440.                 break;
  441.         }
  442.     }
  443.     
  444.     if(MyParams.nDemoType == 1)
  445.     {
  446.         // Clean-up primes array.
  447.         delete[] pPrimesArray;
  448.     }
  449.  
  450.     // Shut down our window, if one was created.
  451.     if(hwndMyThrd)
  452.     {
  453.         DestroyWindow(hwndMyThrd);
  454.         UpdateWindow(hwndMyThrd);
  455.     }
  456.     
  457.     // Now update global with our ending timestamp.  Use mutex object or critical
  458.     // section as specified by user.
  459.     if(!bKillingAllThreads)
  460.     {
  461.         if(MyParams.bUseMutex && hMutex)
  462.         {
  463.             // Wait for mutex.  Write to global memory when we have clearance.
  464.             WaitForSingleObject(hMutex, INFINITE);
  465.         
  466.             dwGlobalThreadLastTimeStamp = GetTickCount();
  467.             nGlobalThreadFinishedCount++;
  468.  
  469.             ReleaseMutex(hMutex);
  470.         }
  471.         else
  472.         {
  473.             // Wait for a critical section.  (The 'critical' code between these two calls 
  474.             // will be run exclusively by the scheduler, guaranteeing mutual exclusion.)
  475.             EnterCriticalSection(&CriticalSectionObj);    
  476.  
  477.             dwGlobalThreadLastTimeStamp = GetTickCount();
  478.             nGlobalThreadFinishedCount++;
  479.  
  480.             LeaveCriticalSection(&CriticalSectionObj);
  481.         }
  482.     }
  483.     _endthread();
  484. }
  485.  
  486. /////////////////////////////////////////////////////////////////////////////
  487.  
  488. // theApp:
  489. // Just creating this application object runs the whole application.
  490. //
  491. CTheApp NEAR theApp;
  492.  
  493. /////////////////////////////////////////////////////////////////////////////
  494.  
  495. // CMainWindow constructor:
  496. // Create the window with the appropriate style, size, menu, etc.
  497. //
  498. CMainWindow::CMainWindow()
  499. {
  500.     cxTextWidth = NULL;
  501.     cyTextHeight = NULL;
  502.  
  503.     // Load our thread dialog to get its dimensions.
  504.     HWND hwndMyThrd = CreateDialog(hInst, 
  505.                                           MAKEINTRESOURCE(IDD_THREAD_WINDOW),
  506.                                           m_hWnd,
  507.                                           (DLGPROC)ThreadDlgProc);
  508.  
  509.     xThrdWnd = 10;
  510.     yThrdWnd = 10;
  511.  
  512.     if(hwndMyThrd)
  513.     {
  514.         RECT rThrdWin;
  515.         ::GetWindowRect(hwndMyThrd, &rThrdWin);
  516.         cxThrdWnd = rThrdWin.right - rThrdWin.left;
  517.         cyThrdWnd = rThrdWin.bottom - rThrdWin.top;
  518.         ::DestroyWindow(hwndMyThrd);
  519.     }
  520.     else
  521.     {
  522.         // Initialize coordinates for thread windows.
  523.         cxThrdWnd = 100;
  524.         cyThrdWnd = 50;
  525.     }
  526.         
  527.     // Create mutex object for later.
  528.     hMutex = CreateMutex((LPSECURITY_ATTRIBUTES)NULL, FALSE, szGlobalMutexName); 
  529.  
  530.     if(!hMutex)
  531.         MessageBox("Unable to create mutex object.  You will have to use 'critical sections' for the thread demo.", "Can't Create Mutex Object", MB_ICONSTOP);
  532.     
  533.     // Prepare for the use of a 'critical section' by multiple threads.
  534.     InitializeCriticalSection(&CriticalSectionObj);
  535.     
  536.     // Initialize our class members to test defaults.
  537.     bFileIODemoStarted = FALSE;
  538.     strWorkFileName = "C:\\TEST.DAT";  // File for read/write test.
  539.     bDoAsynchronousFileIO = TRUE;  // False for synchronous.
  540.     bDoFileWrite = TRUE;  // FALSE = Do Read
  541.     uFileKB = 500;
  542.     
  543.     bThreadDemoStarted = FALSE;
  544.     nThreadCountActual = 0;
  545.         
  546.     cxScreen = GetSystemMetrics(SM_CXSCREEN);
  547.     cyScreen = GetSystemMetrics(SM_CYSCREEN);
  548.  
  549.     // Elapsed time.
  550.     dwTimeStart = 0;
  551.     dwTimeStop = 0;
  552.  
  553.     // For thread demo.
  554.     nThreadCount = 4;
  555.     nTestType = 1;     // 
  556.     dwThreadPriorityClass = NORMAL_PRIORITY_CLASS;
  557.     bUseMutexObjects = TRUE; // FALSE = use Critical Sections.
  558.     bUseWindowPerThread = TRUE;
  559.  
  560.     // For both.    
  561.     bLogOutput = FALSE;
  562.     hfLogFile = HFILE_ERROR;
  563.     strLogFilename = "C:\\THRDLOG.TXT";
  564.         
  565.     // Simple demo. error tracking.
  566.     nErrCode = 0;
  567.     sErrMsg = "";
  568.  
  569.     srand(1234);  // Same seed = same byte info. for file write.
  570.     for(long i=0; i < BUFF_SIZE; i++)
  571.         achGlobalBuff[i] = rand() % 256;
  572.  
  573.     LoadFrame(IDR_MAINFRAME);
  574. }
  575.  
  576. // OnAbout: Display About Dialog box.
  577. void CMainWindow::OnAbout()
  578. {
  579.     CDialog AboutDlg(IDD_ABOUTBOX);
  580.     AboutDlg.DoModal();
  581. }
  582.  
  583. // CMainWindow message map:
  584. // Associate messages with member functions.
  585. //
  586. BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd )
  587.     //{{AFX_MSG_MAP( CMainWindow )
  588.     ON_COMMAND(ID_APP_ABOUT, OnAbout)
  589.     ON_COMMAND(ID_THREAD_DEMO_OPTIONS, OnThreadDemoOptions)
  590.     ON_COMMAND(ID_FILE_IO_OPTIONS, OnFileIOOptions)
  591.     ON_WM_PAINT()
  592.     ON_WM_SIZE()
  593.     ON_WM_CREATE()
  594.     ON_COMMAND(ID_FILE_IO_DEMO, OnDoFileIODemo)
  595.     ON_COMMAND(ID_THREAD_DEMO, OnDoThreadDemo)
  596.     ON_UPDATE_COMMAND_UI(ID_FILE_IO_DEMO, OnUpdateFileIODemo)
  597.     ON_MESSAGE(PM_THREAD_STATUS_MSG, OnThreadStatusMsg)
  598.     ON_MESSAGE(PM_FILE_THREAD_ENDED, OnFileThreadEnded)
  599.     ON_UPDATE_COMMAND_UI(ID_FILE_IO_OPTIONS, OnUpdateFileIOOptions)
  600.     ON_UPDATE_COMMAND_UI(ID_THREAD_DEMO, OnUpdateThreadDemo)
  601.     ON_UPDATE_COMMAND_UI(ID_THREAD_DEMO_OPTIONS, OnUpdateThreadDemoOptions)
  602.     ON_WM_TIMER()
  603.     ON_WM_DESTROY()
  604.     //}}AFX_MSG_MAP
  605. END_MESSAGE_MAP()
  606.  
  607. /////////////////////////////////////////////////////////////////////////////
  608. // CTheApp
  609.  
  610. // InitInstance:
  611. // When any CTheApp object is created, this member function is automatically
  612. // called.  Any data may be set up at this point.
  613. //
  614. // Also, the main window of the application should be created and shown here.
  615. // Return TRUE if the initialization is successful.
  616. //
  617. BOOL CTheApp::InitInstance()
  618. {
  619.     TRACE0("CTheApp::InitInstance\n");
  620.  
  621.     Enable3dControls(); // use 3d controls in dialogs
  622.  
  623.     // Save handle to our instance.
  624.     hInst = m_hInstance;    
  625.  
  626.     m_pMainWnd = new CMainWindow;
  627.     m_pMainWnd->ShowWindow(m_nCmdShow);
  628.     m_pMainWnd->UpdateWindow();
  629.  
  630.     return TRUE;
  631. }
  632.  
  633. void CMainWindow::OnThreadDemoOptions() 
  634. {
  635.     // Change Thread Demo. Options.
  636.  
  637.     CThreadOptionsDlg MyThreadOptionsDlg;
  638.  
  639.     // Load current settings to dialog box variables.
  640.     MyThreadOptionsDlg.m_nThreadCount = nThreadCount;      
  641.     MyThreadOptionsDlg.m_nTestType = nTestType;      
  642.     MyThreadOptionsDlg.m_dwThreadPriorityClass = dwThreadPriorityClass;
  643.     MyThreadOptionsDlg.m_bUseMutexObjects = bUseMutexObjects;
  644.     MyThreadOptionsDlg.m_bUseWindowPerThread = bUseWindowPerThread;
  645.  
  646.     MyThreadOptionsDlg.m_bLogOutput = bLogOutput;
  647.     MyThreadOptionsDlg.m_strLogFilename = strLogFilename;
  648.  
  649.     int rc = MyThreadOptionsDlg.DoModal();
  650.  
  651.     if(rc)
  652.     {
  653.         // Then save new settings to our class members.
  654.         nThreadCount = MyThreadOptionsDlg.m_nThreadCount;      
  655.         nTestType = MyThreadOptionsDlg.m_nTestType;      
  656.         dwThreadPriorityClass = MyThreadOptionsDlg.m_dwThreadPriorityClass;
  657.         bUseMutexObjects = MyThreadOptionsDlg.m_bUseMutexObjects;
  658.         bUseWindowPerThread = MyThreadOptionsDlg.m_bUseWindowPerThread;
  659.     
  660.         bLogOutput = MyThreadOptionsDlg.m_bLogOutput;
  661.         strLogFilename = MyThreadOptionsDlg.m_strLogFilename;
  662.     }
  663. }
  664.  
  665. void CMainWindow::OnFileIOOptions() 
  666. {
  667.     // Change Thread Demo. Options.
  668.     CFileDemoOptionsDlg MyFileDemoOptionsDlg;
  669.  
  670.     // Load current settings to dialog box variables.
  671.     MyFileDemoOptionsDlg.m_strWorkFileName = strWorkFileName;
  672.     MyFileDemoOptionsDlg.m_bDoAsynchronousFileIO = bDoAsynchronousFileIO;  
  673.     MyFileDemoOptionsDlg.m_bDoFileWrite = bDoFileWrite;
  674.     MyFileDemoOptionsDlg.m_uFileKB = uFileKB;
  675.  
  676.     int rc = MyFileDemoOptionsDlg.DoModal();
  677.  
  678.     if(rc)
  679.     {
  680.         // Then save new settings to our class members.
  681.         strWorkFileName = MyFileDemoOptionsDlg.m_strWorkFileName;
  682.         bDoAsynchronousFileIO = MyFileDemoOptionsDlg.m_bDoAsynchronousFileIO;  
  683.         bDoFileWrite = MyFileDemoOptionsDlg.m_bDoFileWrite;
  684.         uFileKB = MyFileDemoOptionsDlg.m_uFileKB;
  685.     }
  686. }
  687.  
  688. void CMainWindow::OnPaint() 
  689. {
  690.     CPaintDC dc(this); // device context for painting
  691.     
  692.     // Gray background.
  693.     RECT r;
  694.  
  695.     GetClientRect(&r);
  696.     
  697.     dc.SelectStockObject(NULL_PEN);
  698.     dc.SelectStockObject(LTGRAY_BRUSH);
  699.  
  700.     dc.Rectangle(0, 0, r.right + 1, r.bottom + 1);
  701.         
  702.     // Do not call CFrameWnd ::OnPaint() for painting messages
  703. }
  704.  
  705. void CMainWindow::OnSize(UINT nType, int cx, int cy) 
  706. {
  707.     CFrameWnd ::OnSize(nType, cx, cy);
  708.     
  709.     // Re-size our label and listbox controls.
  710.     RECT rStatusLB = { cyTextHeight / 2,
  711.                               cyTextHeight * 2,
  712.                               cx - (cyTextHeight / 2),
  713.                               cy - (cyTextHeight * 2) };
  714.  
  715.     StatusLB.MoveWindow(&rStatusLB);
  716.     
  717.     RECT rStatusLBLabel = { cyTextHeight / 2,
  718.                                 cyTextHeight / 2,
  719.                                 cx - (cyTextHeight / 2),
  720.                                 (cyTextHeight * 3) / 2 };
  721.  
  722.     StatusLBLabel.MoveWindow(&rStatusLBLabel);
  723.  
  724.     // Scroll if we can't display at least 70 characters.
  725.     StatusLB.SetHorizontalExtent(70 * cxTextWidth);    
  726.  
  727.     if(cx < cxTextWidth * 35)
  728.     {
  729.         // If we're too small, don't show time in status bar.
  730.         StatusBar.SetPaneInfo( 0, 0, SBPS_NORMAL, cx - cxTextWidth);
  731.         StatusBar.SetPaneInfo( 1, 0, SBPS_NORMAL, 0);
  732.     }
  733.     else
  734.     {
  735.         StatusBar.SetPaneInfo( 0, 0, SBPS_NORMAL, cx - (cxTextWidth * 20));
  736.         StatusBar.SetPaneInfo( 1, 0, SBPS_NORMAL, cxTextWidth * 20);
  737.     }
  738. }
  739.  
  740. int CMainWindow::OnCreate(LPCREATESTRUCT lpCreateStruct) 
  741. {
  742.     if (CFrameWnd ::OnCreate(lpCreateStruct) == -1)
  743.         return -1;
  744.     
  745.     // Get our system font size information.
  746.     TEXTMETRIC tm;
  747.     
  748.     CClientDC dc(this);
  749.  
  750.     dc.GetTextMetrics(&tm);
  751.  
  752.     cyTextHeight = tm.tmHeight;
  753.     cxTextWidth = tm.tmAveCharWidth;
  754.     
  755.     RECT r = { 0, 0, 0, 0 };  // No size -- wait til WM_SIZE to re-size.
  756.  
  757.     // Initialize. our controls that appear in frame window.
  758.     StatusLBLabel.Create("Output :", WS_VISIBLE | SS_LEFT | WS_CHILD, r, this, (unsigned int)IDC_STATIC);
  759.     
  760.     StatusLB.Create(WS_VISIBLE | WS_BORDER | WS_CHILD | WS_VSCROLL | WS_HSCROLL, r, this, IDC_MSG_LB);
  761.  
  762.     StatusBar.Create(this);
  763.     UINT uIDs[2] = { 0, IDC_TIME };
  764.     StatusBar.SetIndicators(uIDs, 2);
  765.  
  766.     return 0;
  767. }
  768.  
  769. void CMainWindow::OnDoFileIODemo() 
  770. {
  771.     char szTemp[256];
  772.  
  773.     CString strFileName;
  774.  
  775.     static FILETHREADPARAMS FileThrdParams;
  776.  
  777.     // Do File I/O demo. with current options.
  778.     bFileIODemoStarted = FALSE;
  779.     //  strWorkFileName;  // File for read/write test.
  780.     //  bDoAsychronousFileIO;  // False for synchronous.
  781.    
  782.     FileThrdParams.hwndParent = m_hWnd;
  783.     
  784.     if(bDoFileWrite)
  785.     { 
  786.         szTemp[256];
  787.  
  788.        if(tmpnam(szTemp) == NULL)
  789.         {
  790.             MessageBox("Unable to create temp. file for writing.", "Can't Create Temp. File", MB_ICONSTOP);
  791.             return;
  792.         }
  793.         
  794.         strFileName = "C:";
  795.         strFileName += szTemp;
  796.         strFileName += "dat";
  797.  
  798.         lstrcpy(FileThrdParams.szFileName, strFileName.GetBuffer(256));
  799.  
  800.         FileThrdParams.dwTotalBytes = (DWORD)uFileKB * 1024L;
  801.  
  802.         // Do a file write.
  803.         if(bDoAsynchronousFileIO)
  804.         {
  805.             wsprintf(szTemp, "Starting File I/O -- Asynchronous Write:");
  806.             AddMsgToLB(szTemp);    
  807.             wsprintf(szTemp, "     %u bytes to '%s'.", FileThrdParams.dwTotalBytes, FileThrdParams.szFileName);  
  808.             AddMsgToLB(szTemp);    
  809.             
  810.             FileThrdParams.bIsAsync = TRUE;
  811.  
  812.             ::SetCursor(::LoadCursor(NULL, IDC_APPSTARTING));
  813.  
  814.             hThrdFileDemo = (HANDLE)_beginthread(FileWriteThreadProc, 16384, (void *)&FileThrdParams);
  815.  
  816.             if(hThrdFileDemo == (HANDLE)-1)
  817.             {
  818.                 ::SetCursor(::LoadCursor(NULL, IDC_APPSTARTING));
  819.                 MessageBox("Unable to create thread for file write.", "Can't Create Thread", MB_ICONSTOP);
  820.                 return;
  821.             }
  822.             else
  823.                 bFileIODemoStarted = TRUE;
  824.         }    
  825.         else
  826.         {
  827.             wsprintf(szTemp, "Starting File I/O -- Synchronous Write:");
  828.             AddMsgToLB(szTemp);    
  829.             wsprintf(szTemp, "     %u bytes to '%s'.", FileThrdParams.dwTotalBytes, FileThrdParams.szFileName);  
  830.             AddMsgToLB(szTemp);    
  831.             
  832.             // Just call thread procedure directly.
  833.             FileThrdParams.bIsAsync = FALSE;
  834.             
  835.             // Hourglass cursor --just like Windows 3.11
  836.             ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
  837.            
  838.            FileWriteThreadProc(&FileThrdParams);
  839.             
  840.             AddMsgToLB("File I/O Demo ended.");    
  841.  
  842.             ::SetCursor(LoadCursor(NULL, IDC_ARROW));
  843.         }
  844.     }
  845.     else
  846.     {
  847.         lstrcpy(FileThrdParams.szFileName, strWorkFileName);
  848.     
  849.         // FALSE = Do Read with provided name.
  850.         if(bDoAsynchronousFileIO)
  851.         {
  852.             wsprintf(szTemp, "Starting File I/O -- Asynchronous Read:");
  853.             AddMsgToLB(szTemp);    
  854.             wsprintf(szTemp, "     file '%s'.", FileThrdParams.szFileName);
  855.             AddMsgToLB(szTemp);    
  856.                         
  857.             FileThrdParams.bIsAsync = TRUE;
  858.             
  859.             hThrdFileDemo = (HANDLE)_beginthread(FileReadThreadProc, 4096, (void *)&FileThrdParams);
  860.  
  861.             if(hThrdFileDemo == (HANDLE)-1)
  862.             {
  863.                 MessageBox("Unable to create thread for file read.", "Can't Create Thread", MB_ICONSTOP);
  864.                 return;
  865.             }
  866.             else
  867.                 bFileIODemoStarted = TRUE;
  868.         }    
  869.         else
  870.         {
  871.             wsprintf(szTemp, "Starting File I/O -- Synchronous Read:");
  872.             AddMsgToLB(szTemp);    
  873.             wsprintf(szTemp, "     file '%s'.", FileThrdParams.szFileName);
  874.             AddMsgToLB(szTemp);    
  875.  
  876.             // Just call thread procedure directly.
  877.             FileThrdParams.bIsAsync = FALSE;
  878.  
  879.             // Hourglass cursor --just like Windows 3.11
  880.             ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
  881.            
  882.            FileReadThreadProc(&FileThrdParams);
  883.             
  884.             AddMsgToLB("File I/O Demo ended.");    
  885.  
  886.             ::SetCursor(LoadCursor(NULL, IDC_ARROW));
  887.         }
  888.     }
  889. }
  890.  
  891. void CMainWindow::OnDoThreadDemo() 
  892. {
  893.     static THREADPARAMS ThrdParams;
  894.  
  895.     int rc;
  896.  
  897.     int i;
  898.  
  899.     char szMsg[256];
  900.     CString strMsg;
  901.  
  902.     if(bThreadDemoStarted)
  903.     {
  904.         bKillingAllThreads = TRUE;  // Setting this flags helps our threads stop processing.
  905.  
  906.         // If demo. is already running, kill off all our threads!
  907.         for(int i = 0; i < 64; i++)
  908.             if(hThrd[i] != (HANDLE)-1)
  909.                 CloseHandle(hThrd[i]);
  910.                 
  911.         AddMsgToLB("Thread Demo. stopped by user.");            
  912.     
  913.         bThreadDemoStarted = FALSE;
  914.  
  915.         KillTimer(5555);
  916.  
  917.         SetTimeMsg("");
  918.  
  919.         // Restore process priority to 'normal.'
  920.         HANDLE hMyProcess = GetCurrentProcess();
  921.     
  922.         ASSERT (hMyProcess);
  923.             
  924.         rc = SetPriorityClass(hMyProcess, NORMAL_PRIORITY_CLASS);
  925.         if(!rc)
  926.         {
  927.             MessageBox("Unable to restore thread priority class.  Threads will run in the default 'normal' priority class.",
  928.                   "Can't Restore Thread Priority", MB_ICONEXCLAMATION);
  929.         }
  930.  
  931.         if(hfLogFile != HFILE_ERROR)
  932.         {
  933.             _lclose(hfLogFile);
  934.             hfLogFile = HFILE_ERROR;
  935.         }
  936.  
  937.         return;
  938.     }
  939.  
  940.     
  941.     bKillingAllThreads = FALSE;
  942.  
  943.     if(bLogOutput)
  944.     {
  945.         if((hfLogFile = _lopen(strLogFilename.GetBuffer(256), OF_WRITE)) != HFILE_ERROR)
  946.         {
  947.             // If file exists, append to end of file.
  948.             _llseek(hfLogFile, 0, FILE_END); 
  949.         }    
  950.         else
  951.         {
  952.             // Try to create new file, if none exists.
  953.             hfLogFile = _lcreat(strLogFilename.GetBuffer(256), 0);
  954.         }
  955.         
  956.         if(hfLogFile == HFILE_ERROR)
  957.         {    
  958.             wsprintf(szMsg, "Can't open log file '%s'.  Output will not be saved.", strLogFilename.GetBuffer(256));
  959.             MessageBox(szMsg, "Can't Open Log File", MB_ICONEXCLAMATION);            
  960.         }
  961.     }
  962.  
  963.     xThrdWnd = 10;
  964.     yThrdWnd = 10;
  965.     
  966.     nThreadCountActual = 0;
  967.     
  968.     ThrdParams.nDemoType = nTestType;
  969.     if(nTestType == 4)
  970.         // All demo. tests are timed except untimed graphics.
  971.         ThrdParams.bIsTimed = FALSE;
  972.     else
  973.         ThrdParams.bIsTimed = TRUE;
  974.  
  975.     ThrdParams.bUseWindow = bUseWindowPerThread;
  976.  
  977.     ThrdParams.hwndParent = m_hWnd;
  978.  
  979.     ThrdParams.bUseMutex = bUseMutexObjects;
  980.             
  981.     // Clear thread handle array.                          
  982.     memset(hThrd, -1, sizeof(HANDLE) * 64);
  983.     
  984.     HANDLE hMyProcess = GetCurrentProcess();
  985.     
  986.     ASSERT (hMyProcess);
  987.     
  988.     // This is also a way to get the current process handle.
  989.     // DWORD ProcessId = GetCurrentProcessId();
  990.     // HANDLE hMyProcess = OpenProcess(PROCESS_ALL_ACCESS, TRUE, ProcessId);
  991.                                 
  992.     // Reset globals used to collect info. from individual threads.
  993.     dwGlobalThreadLastTimeStamp = 0L;
  994.     nGlobalThreadFinishedCount = 0L;
  995.  
  996.     // Start timing here!
  997.     if(ThrdParams.bIsTimed)
  998.         dwTimeStart = GetTickCount();
  999.             
  1000.     DWORD dwStackSize = 8192;
  1001.  
  1002.      for(i = 0; i < nThreadCount; i++)
  1003.     {
  1004.         if(ThrdParams.bUseWindow)
  1005.         {
  1006.             ThrdParams.ptWinStart.x = xThrdWnd;
  1007.             ThrdParams.ptWinStart.y = yThrdWnd;
  1008.         }
  1009.         else
  1010.         {
  1011.             ThrdParams.ptWinStart.x = -1;
  1012.             ThrdParams.ptWinStart.y = -1;
  1013.         }
  1014.         
  1015.         // Create each thread with desired priority class.
  1016.         ThrdParams.nThreadNum = i + 1;
  1017.  
  1018.         // Pass our thread param. as a global object.
  1019.         // (This ensures that threads find the right params.)
  1020.         HGLOBAL hGlobal =    GlobalAlloc(GHND, sizeof(ThrdParams));
  1021.         
  1022.         LPSTR pThrdParamsGlobalBlock = (LPSTR)GlobalLock(hGlobal);
  1023.  
  1024.         if(!pThrdParamsGlobalBlock)
  1025.             MessageBox("Can't allocate memory to initialize thread.", "Not Enough Memory", MB_ICONSTOP); 
  1026.         else
  1027.         {
  1028.             memcpy(pThrdParamsGlobalBlock, &ThrdParams, sizeof(ThrdParams));    
  1029.             hThrd[i] = (HANDLE)_beginthread(ThreadDemoProc, dwStackSize, (void *)pThrdParamsGlobalBlock);
  1030.         }
  1031.  
  1032.         if(hThrd[i] == (HANDLE)-1)
  1033.         {
  1034.             MessageBox("Unable to create thread #%d for demo.  Try running demo with fewer threads or reduce system memory usage and then try again.", "Can't Create Thread", MB_ICONSTOP);
  1035.             return;
  1036.         }
  1037.         else
  1038.         {
  1039.             SuspendThread(hThrd[i]);
  1040.     
  1041.             bThreadDemoStarted = TRUE;
  1042.             nThreadCountActual++;
  1043.             if(ThrdParams.bUseWindow)
  1044.             {
  1045.                 yThrdWnd += cyThrdWnd + 1;  // Next thread window would appear here.
  1046.                 if(yThrdWnd + cyThrdWnd > cyScreen)
  1047.                 {
  1048.                     yThrdWnd = 10;
  1049.                     xThrdWnd +=    cxThrdWnd + 1;
  1050.                     if(xThrdWnd + cxThrdWnd > cxScreen)
  1051.                     {
  1052.                         // Windows will 'tile' at beginning of screen after lower left corner 
  1053.                         // has been reached.
  1054.                         xThrdWnd = 10;
  1055.                         yThrdWnd = 10;
  1056.                     }
  1057.                 }
  1058.             }
  1059.         }
  1060.     }
  1061.     
  1062.     rc = SetPriorityClass(hMyProcess, dwThreadPriorityClass);
  1063.     if(!rc)
  1064.     {
  1065.         MessageBox("Unable to change thread priority class.  Threads will run in the default 'normal' priority class.",
  1066.                       "Can't Set Thread Priorities", MB_ICONEXCLAMATION);
  1067.     }
  1068.  
  1069.     // Resume all threads.
  1070.     for(i = 0; i < 64; i++)
  1071.     {
  1072.         if(hThrd[i] != (HANDLE)-1)
  1073.             ResumeThread(hThrd[i]);    
  1074.     }    
  1075.                 
  1076.     if(nThreadCountActual == 0 && !bThreadDemoStarted)
  1077.     {
  1078.         AddMsgToLB("No threads were created!  Demo stopped.");    
  1079.         return;
  1080.     }
  1081.  
  1082.     if(ThrdParams.nDemoType == 1)
  1083.         strMsg =    "Starting Thread Demo. -- 1000 Primes with ";
  1084.     else if(ThrdParams.nDemoType == 2)
  1085.         strMsg =    "Starting Thread Demo. -- Floating-Point Calculation with ";
  1086.     else if(ThrdParams.nDemoType == 3)
  1087.         strMsg =    "Starting Thread Demo. -- Timed Graphics with ";
  1088.     else // default is untimed graphics.
  1089.         strMsg =    "Starting Thread Demo. -- Untimed Graphics with ";
  1090.  
  1091.     if(nThreadCountActual == 1)
  1092.         wsprintf(szMsg, "1 thread %s", 
  1093.                     ThrdParams.bUseWindow ? "(windowed)." : ".");
  1094.     else
  1095.         wsprintf(szMsg, "%d threads %s", nThreadCountActual, 
  1096.                     ThrdParams.bUseWindow ? "(windowed)." : ".");
  1097.     
  1098.     strMsg += szMsg;    
  1099.  
  1100.     AddMsgToLB(strMsg);    
  1101.  
  1102.     if(ThrdParams.bIsTimed)
  1103.     {
  1104.         // If we're timed, start a timer which will check on the
  1105.         // status of each thread.  The granularity here is larger (.900 sec.)
  1106.         // than what we'll get from each thread.  (As each thread exits,
  1107.         // it will write its time to the global thread variables in millisec.);
  1108.         SetTimer(5555, 900, NULL);
  1109.     }
  1110. }
  1111.  
  1112. LRESULT CMainWindow::OnThreadStatusMsg(WPARAM uUnused, LPARAM lpszMsg)
  1113. {
  1114.     // Process status message from our thread.
  1115.     StatusBar.SetWindowText((LPSTR)lpszMsg);
  1116.     StatusBar.UpdateWindow();
  1117.  
  1118.     return 0L;                                      
  1119. }
  1120.  
  1121. LRESULT CMainWindow::OnFileThreadEnded(WPARAM uUnused, LPARAM lResult)
  1122. {
  1123.     if(bFileIODemoStarted)
  1124.     {
  1125.         if(lResult)
  1126.             AddMsgToLB("File I/O Demo completed OK.");    
  1127.         else
  1128.             AddMsgToLB("File I/O Demo ended with an error.");    
  1129.     }
  1130.     bFileIODemoStarted = FALSE;            
  1131.  
  1132.     return 0L;
  1133. }
  1134.  
  1135. void CMainWindow::OnUpdateFileIOOptions(CCmdUI* pCmdUI) 
  1136. {
  1137.     if(!bFileIODemoStarted && !bThreadDemoStarted)    
  1138.         pCmdUI->Enable(TRUE);
  1139.     else
  1140.         pCmdUI->Enable(FALSE);
  1141. }
  1142.  
  1143. void CMainWindow::OnUpdateThreadDemoOptions(CCmdUI* pCmdUI) 
  1144. {
  1145.     if(!bFileIODemoStarted && !bThreadDemoStarted)    
  1146.         pCmdUI->Enable(TRUE);
  1147.     else
  1148.         pCmdUI->Enable(FALSE);
  1149. }
  1150.  
  1151. void CMainWindow::OnUpdateFileIODemo(CCmdUI* pCmdUI) 
  1152. {
  1153.     if(!bFileIODemoStarted && !bThreadDemoStarted)    
  1154.         pCmdUI->Enable(TRUE);
  1155.     else
  1156.         pCmdUI->Enable(FALSE);
  1157. }
  1158.  
  1159. void CMainWindow::OnUpdateThreadDemo(CCmdUI* pCmdUI) 
  1160. {
  1161.     if(!bFileIODemoStarted)
  1162.     {
  1163.          if(bThreadDemoStarted)    
  1164.         {
  1165.             // Change text to stop demo.
  1166.             pCmdUI->SetText("Stop Thread Demo.");                 
  1167.         }
  1168.         else
  1169.             // Change text to stop demo.
  1170.             pCmdUI->SetText("Start Thread Demo.");                 
  1171.  
  1172.         pCmdUI->Enable(TRUE);
  1173.     }
  1174.     else
  1175.         pCmdUI->Enable(FALSE);
  1176. }
  1177.  
  1178. LPSTR CMainWindow::GetElapsedTimeAsStr(DWORD dwEarlier, DWORD dwLater)
  1179. {
  1180.     static char szTime[80];
  1181.  
  1182.     DWORD dwElapsed = dwLater - dwEarlier; 
  1183.  
  1184.     int Min = dwElapsed / 60000;
  1185.     
  1186.     int Hrs = Min / 60;
  1187.  
  1188.     Min = Min % 60;
  1189.  
  1190.     float Sec = (float)(dwElapsed % 60000);
  1191.  
  1192.     Sec /= (float)1000.0;
  1193.  
  1194.     sprintf(szTime, "%02d:%02d:%05.2f", Hrs, Min, Sec);
  1195.  
  1196.     return szTime;
  1197. }
  1198.  
  1199. void CMainWindow::ShowElapsedTime(DWORD dwEarlier, DWORD dwLater)
  1200. {
  1201.     LPSTR lpszTime = GetElapsedTimeAsStr(dwEarlier, dwLater);
  1202.  
  1203.     SetTimeMsg(lpszTime);
  1204. }
  1205.  
  1206. void CMainWindow::OnTimer(UINT nIDEvent) 
  1207. {
  1208.     DWORD dwNow;
  1209.  
  1210.     char szMsg[256];
  1211.  
  1212.     int nCompletedThreads = 0;
  1213.  
  1214.     if(nIDEvent == 5555)
  1215.     {
  1216.         // Update time in status bar.
  1217.         if(bUseMutexObjects && hMutex)
  1218.             // Wait until we have exclusive access to this global memory location.
  1219.         {
  1220.             // Wait for mutex.  Write to global memory when we have clearance.
  1221.             WaitForSingleObject(hMutex, INFINITE);
  1222.         
  1223.             if(nGlobalThreadFinishedCount >= nThreadCountActual)
  1224.             {
  1225.                 // Then we're finished.  All threads have run to completion.
  1226.                 dwTimeStop = dwGlobalThreadLastTimeStamp;
  1227.                 bThreadDemoStarted = FALSE;                        
  1228.             }
  1229.             ReleaseMutex(hMutex);
  1230.         }
  1231.         else
  1232.         {
  1233.             // Wait for a critical section.  (The 'critical' code between these two calls 
  1234.             // will be run exclusively by the scheduler, guaranteeing mutual exclusion.)
  1235.             EnterCriticalSection(&CriticalSectionObj);    
  1236.  
  1237.             if(nGlobalThreadFinishedCount >= nThreadCountActual)
  1238.             {
  1239.                 // Then we're finished.  All threads have run to completion.
  1240.                 dwTimeStop = dwGlobalThreadLastTimeStamp;
  1241.                 bThreadDemoStarted = FALSE;                        
  1242.             }
  1243.             
  1244.             LeaveCriticalSection(&CriticalSectionObj);
  1245.         }
  1246.         
  1247.         if(bThreadDemoStarted)
  1248.         {
  1249.             // If we're still working, update elapsed time and thread activity.
  1250.             dwNow = GetTickCount();    
  1251.             ShowElapsedTime(dwTimeStart, dwNow);
  1252.  
  1253.             wsprintf(szMsg, "%d out of %d thread(s) still working...", 
  1254.                 nThreadCountActual - nGlobalThreadFinishedCount, nThreadCountActual);            
  1255.  
  1256.             SetStatusMsg(szMsg);
  1257.         }
  1258.         else
  1259.         {
  1260.             // If we're finished, report final elapsed time and clear timer window.
  1261.             wsprintf(szMsg, "Test finished: %d Threads completed, Elapsed time was %s",
  1262.                         nGlobalThreadFinishedCount, 
  1263.                         GetElapsedTimeAsStr(dwTimeStart, dwTimeStop));
  1264.  
  1265.             AddMsgToLB(szMsg);
  1266.  
  1267.             // Stop our 'sentinel' timer as well.
  1268.             KillTimer(5555);
  1269.  
  1270.             SetTimeMsg("");
  1271.  
  1272.             // Restore process priority to 'normal.'
  1273.             HANDLE hMyProcess = GetCurrentProcess();
  1274.     
  1275.             ASSERT (hMyProcess);
  1276.             
  1277.             int rc = SetPriorityClass(hMyProcess, NORMAL_PRIORITY_CLASS);
  1278.             if(!rc)
  1279.             {
  1280.                 MessageBox("Unable to restore thread priority class.  Threads will run in the default 'normal' priority class.",
  1281.                       "Can't Restore Thread Priority", MB_ICONEXCLAMATION);
  1282.             }
  1283.  
  1284.             if(hfLogFile)
  1285.             {
  1286.                 _lclose(hfLogFile);
  1287.                 hfLogFile = NULL;
  1288.             }
  1289.         }
  1290.     }
  1291.     else
  1292.         CFrameWnd ::OnTimer(nIDEvent);
  1293. }
  1294.  
  1295. void CMainWindow::AddMsgToLB(CString& strMsg, BOOL bShowInStatusBarToo) 
  1296. {
  1297.     int rc = StatusLB.AddString(strMsg);  
  1298.     if(rc > -1)
  1299.         StatusLB.SetSel(rc);
  1300.  
  1301.     StatusLB.UpdateWindow(); 
  1302.     if(bShowInStatusBarToo) 
  1303.     { 
  1304.         StatusBar.SetWindowText(strMsg.GetBuffer(255));
  1305.         StatusBar.UpdateWindow(); 
  1306.     }
  1307.     if(hfLogFile)
  1308.     {
  1309.         char szLine[300];
  1310.         wsprintf(szLine, "%s\n\r", strMsg.GetBuffer(256));
  1311.         _lwrite(hfLogFile, szLine, lstrlen(szLine));
  1312.     }     
  1313. }            
  1314.  
  1315. void CMainWindow::OnDestroy() 
  1316. {
  1317.     if(hMutex)
  1318.         CloseHandle(hMutex);
  1319.  
  1320.     if(hfLogFile != HFILE_ERROR)
  1321.         _lclose(hfLogFile);
  1322.  
  1323.     CFrameWnd::OnDestroy();
  1324. }
  1325.